package org.zzq.my12306.biz.gatewayservice.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.zzq.my12306.biz.gatewayservice.config.Config;
import org.zzq.my12306.frameworks.starter.base.constant.UserConstant;
import org.zzq.my12306.frameworks.starter.user.core.UserInfoDTO;
import org.zzq.my12306.frameworks.starter.user.toolkit.JWTUtil;

import java.lang.invoke.VarHandle;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * SpringCloud Gateway 过滤器，用于对黑名单的路径进行 JWT 验证
 */
@Component
@Slf4j
public class TokenValidateGatewayFilterFactory extends AbstractGatewayFilterFactory<Config> {
    public TokenValidateGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 用户注销的路径(网关验证身份后只需要往对应微服务模块传递用户相关的信息就可以了，但是如果用户要注销，还要把 Token 传递出去)
     */
    public static final String DELETION_PATH = "/api/user-service/deletion";

    @Override
    public GatewayFilter apply(Config config) {
        // log.info("{}, {}", config.getBlackPathPre(), System.currentTimeMillis());
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String requestPath = request.getPath().toString();
            // 黑名单路径需要进行 JWT 验证
            if (isPathInBlackPathPreList(requestPath, config.getBlackPathPre())) {
                String token = request.getHeaders().getFirst("Authorization");
                // TODO 需要验证 Token 是否有效，有可能用户注销了账号，但是 Token 有效期还没过
                UserInfoDTO userInfoDTO = JWTUtil.parseJwtToken(token);
                // 用户信息错误，返回状态码 401 UNAUTHORIZED
                if (!validateToken(userInfoDTO)) {
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return response.setComplete();
                }
                // 封装用户信息到 HTTP 头部，传递给后面的微服务模块
                ServerHttpRequest.Builder builder = exchange.getRequest().mutate().headers(HttpHeaders -> {
                    HttpHeaders.set(UserConstant.USER_ID_KEY, userInfoDTO.getUserId());
                    HttpHeaders.set(UserConstant.USER_NAME_KEY, userInfoDTO.getUsername());
                    HttpHeaders.set(UserConstant.REAL_NAME_KEY, URLEncoder.encode(userInfoDTO.getRealName(), StandardCharsets.UTF_8));
                    // 如果路径是注销用户，还需要传递 Token 信息
                    if (requestPath.equals(DELETION_PATH)) {
                        HttpHeaders.set(UserConstant.USER_TOKEN_KEY, token);
                    }
                });
                return chain.filter(exchange.mutate().request(builder.build()).build());
            }
            // 白名单，直接传入到下一个过滤器
            return chain.filter(exchange);
        };
    }

    private boolean isPathInBlackPathPreList(String requestPath, List<String> blackPathPre) {
        if (CollectionUtils.isEmpty(blackPathPre)) {
            return false;
        }
        return blackPathPre.stream().anyMatch(requestPath::startsWith);
    }

    private boolean validateToken(UserInfoDTO userInfoDTO) {
        return userInfoDTO != null;
    }
}
